#ifndef MODEL_GENERATOR_H_JM_20120809
#define MODEL_GENERATOR_H_JM_20120809
/** @file ModelGenerator.h
 * Zakladna trieda pre generatory modelov vypoctu boboxu.
 **/
//  ##
#include "IdGenerators.h"
#include "SliceGenerator.h"
#include "BoxDescriptor.h"
//  ## C++
#include <string>
#include <memory>
#include <vector>

namespace jm4bbx {

	/**
	 * Implementacia generatora identifikatorov pre txt. model vypoctu boboxu 
	 * pre boxy aj spojenia.
	 **/
	class ModelIdGenerator : public BoxIdGenerator, public ViaIdGenerator {
	public:
		ModelIdGenerator() : boxId_(0), viaId_(0) { } 
		virtual int getNextBoxId() override;
		virtual int getNextViaId() override;
		virtual void resetGenerator();
	private:
		/*static*/ int boxId_/* = 0*/;
		/*static*/ int viaId_/* = 0*/;

	};


	/**
	 * Bazova trieda pre generatory modelov. Trieda poskytuje jednoduche 
	 * nastroje, pomocou ktorych je mozne jednoduch vytvorit generator modelu 
	 * pre vypocet boboxu.
	 * Metody addBox(...) a addSlice umoznuju jednoduche vytvorenie modelu. 
	 * Pomocou addBox(...) je mozne pridata a pripadne aj vytvorit popisy 
	 * boxov modelu. Nasledne je mozne definovat vzajomne spojenia boxov 
	 * pomocou addSlice(...), ktore pridavaju spojenia boxov.
	 * Generator automaticky pridava vstupny a vystupny box modelu, je vsak 
	 * potrebne ich napojit na ostatne boxy modelu.
	 **/
	class ModelGenerator {
	public:
		ModelGenerator();



		/**
		 * Priadnie definicie boxu do modelu spolu s vytvorenim popisu boxu.
		 * @param boxName meno definicie boxu v modele
		 * @param boxParams parametre definicie boxu v modele
		 * @param repetition nasobnost boxu v modele
		 * @return popis pridanej definicie boxu do modelu
		 **/
		BoxDescriptorPtr addBox(const std::string& boxName, 
			const std::string& boxParams, int repetition = 1);

		/**
		 * Priadnie definicie boxu do modelu spolu s vytvorenim popisu boxu.
		 * @param boxName meno definicie boxu v modele
		 * @param repetition nasobnost boxu v modele
		 * @return popis pridanej definicie boxu do modelu
		 **/
		BoxDescriptorPtr addBox(const std::string& boxName, int repetition = 1);

		/**
		 * Priadnie definicie boxu do modelu.
		 * @param box popis definice boxu 
		 * @param repetition nasobnost boxu v modele
		 **/
		void addBox(const BoxDescriptorPtr& box, int repetition = 1);

		/**
		 * Pridanie rezu spojeni do modelu.
		 * @tparam SliceType typ vytvaraneho rezu spojeni
		 * @param srcId id zdrojoveho boxu
		 * @param dstId id cieloveho boxu
		 **/
		template<class SliceType>
		void addSlice(const BoxDescriptor::BoxId& srcId, 
			const BoxDescriptor::BoxId& dstId, const std::string& params = "") {
				addSlice(SliceGeneratorPtr(
					new SliceType(srcId, dstId, params)));
		}

	    /**
		 * Pridanie rezu spojeni do modelu.
		 * @tparam SliceType typ vytvaraneho rezu spojeni
		 * @param srcIds id zdrojovych boxov
		 * @param dstId id cieloveho boxu
		 **/
		template<class SliceType>
		void addSlice(const BoxDescriptor::BoxIds& srcIds, 
			const BoxDescriptor::BoxId& dstId, const std::string& params = "") {
				addSlice(SliceGeneratorPtr(
					new SliceType(srcIds, dstId, params)));
		}
		
		/**
		 * Pridanie rezu spojeni do modelu.
		 * @tparam SliceType typ vytvaraneho rezu spojeni
		 * @param srcId id zdrojoveho boxu
		 * @param dstIds id cielovych boxov
		 **/
		template<class SliceType>
		void addSlice(const BoxDescriptor::BoxId& srcId, 
			const BoxDescriptor::BoxIds& dstIds, const std::string& params = "") {
				addSlice(SliceGeneratorPtr(
					new SliceType(srcId, dstIds, params)));
		}
		
		/**
		 * Pridanie rezu spojeni do modelu.
		 * @tparam SliceType typ vytvaraneho rezu spojeni
		 * @param srcIds id zdrojovych boxov
		 * @param dstIds id cielovych boxov
		 **/
		template<class SliceType>
		void addSlice(const BoxDescriptor::BoxIds& srcIds, 
			const BoxDescriptor::BoxIds& dstIds, const std::string& params = "") {
				addSlice(SliceGeneratorPtr(
					new SliceType(srcIds, dstIds, params)));
		}

		/**
		 * Pridanie rezu spojeni do modelu.
		 * @param sg pridavany rez
		 **/
		inline void addSlice(SliceGenerator* sg) {
			slices_.push_back(SliceGeneratorPtr(sg));
		}

		/**
		 * Vygeneruj model vypoctu.
		 **/
		std::string generateModel(const std::string& lineEnd = "");

		/// Id vstupneho boxu do modelu.
		int getModelInputId() const;
		/// Id vystupneho boxu z modelu.
		int getModelOutputId() const;

	protected:
		/// Meno vstupneho boxu - dane boboxom.
		static const char* START_BOX_NAME;
		/// Meno vystupneho boxu - dane boboxom.
		static const char* END_BOX_NAME;
	private:
		/// Generator identifikatorv boxov a spojeni v modele.
		ModelIdGenerator idGenerator_;
		/// Sada boxov modelu.
		std::vector<BoxDescriptorPtr> boxes_;
		/// Sada rezov spojeni modelu.
		std::vector<SliceGeneratorPtr> slices_;
	};



} // namespace jm4bbx

#endif
